use chrono::{NaiveDate, NaiveDateTime};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use yew_store::Update;

pub enum Mode {
    // Login,
    Index,
}

pub struct State {
    pub mode: Mode,
    pub tasks: BTreeMap<NaiveDate, Vec<Task>>,
    pub tasks_number: usize,
    // pub comments: BTreeMap<NaiveDate, Vec<Comment>>,
    pub comments: Vec<Comment>,
    pub comments_number: usize,
    pub selected_task_id: Option<i32>,
}

impl State {
    pub fn new() -> Self {
        Self {
            mode: Mode::Index,
            tasks: BTreeMap::new(),
            // comments: BTreeMap::new(),
            comments: Vec::new(),
            selected_task_id: None,
            tasks_number: 0,
            comments_number: 0,
        }
    }
}

pub enum Action {
    ChangeMode(Mode),
    SetTasks(Vec<Task>),
    SetComments(Vec<Comment>),
    SetSelectedTask(Option<i32>),
}

#[derive(Clone, Debug)]
pub enum Event {
    ModeChanged,
    TasksChanged,
    CommentsChanged,
    SelectedTaskChanged,
}

impl Update<State> for Action {
    type Event = Event;

    fn update(self, state: &mut State) -> Option<Self::Event> {
        match self {
            Action::ChangeMode(mode) => {
                state.mode = mode;
                Some(Event::ModeChanged)
            }
            Action::SetTasks(mut tasks) => {
                tasks.sort_by_key(|t| t.created_at);
                state.tasks_number = tasks.len();

                state.tasks.clear();

                tasks.into_iter().for_each(|t| {
                    state.tasks.entry(t.created_at.date()).or_default().push(t);
                });
                Some(Event::TasksChanged)
            }
            Action::SetComments(comments) => {
                // comments.sort_by_key(|c| c.created_at);
                // state.comments_number = comments.len();

                // state.comments.clear();

                // comments.into_iter().for_each(|c| {
                //     state
                //         .comments
                //         .entry(c.created_at.date())
                //         .or_default()
                //         .push(c);
                // });
                state.comments = comments;
                Some(Event::CommentsChanged)
            }
            Action::SetSelectedTask(new) => {
                state.selected_task_id = new;
                Some(Event::SelectedTaskChanged)
            }
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Error {
    pub error: String,
}

impl Error {
    pub fn new(msg: &str) -> Self {
        Self { error: msg.into() }
    }
}

impl From<failure::Error> for Error {
    fn from(error: failure::Error) -> Self {
        Error::new(&error.to_string())
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Task {
    pub id: i32,
    pub description: String,
    pub completed: bool,
    pub created_at: NaiveDateTime,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Comment {
    pub id: i32,
    pub description: String,
    pub task_id: i32,
    pub created_at: NaiveDateTime,
}
